package za.org.jcicada.datastore.impl;

import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import za.org.jcicada.cfgmngr.IConfigurationClient;
import za.org.jcicada.cfgmngr.IConfigurationServer;
import za.org.jcicada.cfgmngr.ISystemObserver;
import za.org.jcicada.cfgmngr.ISystemSubject;
import za.org.jcicada.cfgmngr.impl.CfgMngr;
import za.org.jcicada.cfgmngr.impl.SystemSubject;
import za.org.jcicada.genss.ServiceURN;
import za.org.jcicada.genss.SystemURN;
import za.org.jcicada.genss.sensor.ISensorSubject;
import za.org.jcicada.genss.sensor.impl.InvalidSensorValue;
import za.org.jcicada.genss.sensor.impl.Sensor;
import za.org.jcicada.genss.sensor.impl.StatusStrategyFactory;
import za.org.jcicada.genss.sensor.impl.TypeInt;

public class Model implements DataStore.ICallback {

    /** Class logger */
    private static final Log logger = LogFactory.getLog(Model.class);
    
    private IConfigurationServer configurationServer;
	private IConfigurationClient configurationClient;
	private DataStoreServerAbstract dataStoreServer;
	private ISystemSubject systemSubject;
	protected ISystemObserver systemObserver;
	protected Map<ServiceURN, SensorObserver> sensorObservers;
	private Timer timer;
	//:TODO combine rateSensor, counter and currentCounter into an object
	private Sensor rateSensor;
	private Sensor statusRateSensor;
	private int counter;
	private int statusCounter;
	
	public Model(IConfigurationServer configurationServer, IConfigurationClient configurationClient, Timer timer) {
		this.configurationServer = configurationServer;
		this.configurationClient = configurationClient;
		this.rateSensor = new Sensor(TypeInt.getInstance(0, Integer.MAX_VALUE), "updates", "Number of DataStore updates per second", "Hz");
		this.statusRateSensor = new Sensor(TypeInt.getInstance(0, Integer.MAX_VALUE), "statusupdates", "Number of DataStore status updates per second", "Hz");
		this.timer = timer;
	}
	
	public void initialize(DataStoreServerAbstract dataStoreServer, DataStoreDynamicConfiguration config) throws URISyntaxException {
		this.dataStoreServer = dataStoreServer;
		rateSensor.setStatusStrategy(StatusStrategyFactory.createStatusStrategy(rateSensor, config.rateStatusStrategy));
		statusRateSensor.setStatusStrategy(StatusStrategyFactory.createStatusStrategy(statusRateSensor, config.statusRateStatusStrategy));
		sensorObservers = new HashMap<ServiceURN, SensorObserver>();
		systemObserver = new SystemObserver(configurationServer, configurationClient);
		ServiceURN serviceURN = new ServiceURN(new SystemURN(CfgMngr.SYSTEM_NAME), SystemSubject.URN_SERVICE);
		configurationServer.requestLocateService(configurationClient, serviceURN, "tcp");
		counter = 0;
		statusCounter = 0;
		timer.scheduleAtFixedRate(new TimerTask() {

			@Override
			public void run() {
				int currentCounter = 0;
				int currentStatusCounter = 0;
				for (SensorObserver sensorObserver : sensorObservers.values()) {
					currentCounter += sensorObserver.getCounter();
					currentStatusCounter += sensorObserver.getStatusCounter();
				}
				try {
					rateSensor.setValue(new Integer(currentCounter - counter));
					statusRateSensor.setValue(new Integer(currentStatusCounter - statusCounter));
					
				} catch (InvalidSensorValue e) {
					logger.error(e);
				}
				counter = currentCounter;
				statusCounter = currentStatusCounter;
			}
			
		}, 0L, 1000L);
	}
	
	public void shutdown() {
		if (systemSubject != null) {
			systemSubject.informDisconnect(systemObserver);
		}
		for (Map.Entry<ServiceURN, SensorObserver> entry : sensorObservers.entrySet()) {
			entry.getValue().shutdown();
		}
		sensorObservers.clear();
		sensorObservers = null;
	}
	
	public Sensor[] getSensors() {
		return new Sensor[] {rateSensor, statusRateSensor};
	}
	
	public void setSystemSubject(ISystemSubject systemSubject) {
		this.systemSubject = systemSubject;
		systemSubject.requestAttach(systemObserver);
	}

	public void addSensorSubject(ServiceURN serviceURN, ISensorSubject sensorSubject) {
		if (!sensorObservers.containsKey(serviceURN)) {
			SensorObserver sensorObserver = new SensorObserver(sensorSubject, dataStoreServer);
			sensorObservers.put(serviceURN, sensorObserver);
			sensorObserver.initialize();
		}
	}
	
}
